package com.h2o3.carmack.message;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;

import com.google.common.base.Preconditions;
import com.google.common.reflect.AbstractInvocationHandler;
import com.google.common.reflect.Reflection;
import com.h2o3.carmack.message.annotation.Handler;
import com.h2o3.carmack.message.annotation.HandlerMapping;
import com.h2o3.carmack.session.Session;

/**
 * @author chensheng
 * @version $Id: MessageHandlerRegister.java, v 0.1 1/18/14 9:56 PM chensheng Exp $
 */
public class MessageHandlerRegister implements BeanFactoryAware, BeanPostProcessor {

    private BeanFactory       beanFactory;

    private MessageDispatcher messageDispatcher;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
                                                                               throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName)
                                                                                    throws BeansException {
        if (bean.getClass().isAnnotationPresent(Handler.class)) {
            if (bean instanceof MessageHandler
                && bean.getClass().isAnnotationPresent(HandlerMapping.class)) {
                String code = bean.getClass().getAnnotation(HandlerMapping.class).value();

                getMessageDispatcher().registerHandler(code, (MessageHandler) bean);
            } else {
                for (final Method method : bean.getClass().getMethods()) {
                    if (Modifier.isPublic(method.getModifiers())
                        && method.isAnnotationPresent(HandlerMapping.class)) {
                        String code = method.getAnnotation(HandlerMapping.class).value();

                        Class<?>[] parameterTypes = method.getParameterTypes();
                        Preconditions.checkArgument(
                            parameterTypes.length == 2
                                    && parameterTypes[0].isAssignableFrom(Session.class)
                                    && parameterTypes[1].isAssignableFrom(Message.class),
                            "HandlerMapping标注的方法参数类型必须为[Session, JSONObject]");
                        getMessageDispatcher().registerHandler(code, Reflection.newProxy(
                            MessageHandler.class, new AbstractInvocationHandler() {
                                @Override
                                protected Object handleInvocation(Object proxy,
                                                                  Method interfaceMethod,
                                                                  Object[] args) throws Throwable {
                                    return method.invoke(bean, args);
                                }
                            }));
                    }
                }
            }
        }
        return bean;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    protected MessageDispatcher getMessageDispatcher() {
        if (messageDispatcher == null) {
            messageDispatcher = beanFactory.getBean(MessageDispatcher.class);
        }
        return messageDispatcher;
    }
}
